﻿using UnityEngine;
using System;

namespace UnityModule
{
    public enum PrefabReleaseDisappearType
    {
        ActiveFalse,
        ScaleZero,
        MoveOut,
    }

    [System.Serializable]
    public class PrefabPool
    {
        /// <summary>
        /// 最大缓存多少个
        /// </summary>
        private int m_CullAbove = 50;

        /// <summary>
        /// 当小池超过最大数量 m_SmoothDestroy = false 直接删除, 否则会传到m_NeedDestroys
        /// </summary>
        public bool m_SmoothDestroy = false;

        /// <summary>
        /// 当前池中对象个数
        /// </summary>
        public int ObjCount { get { return m_CurIndex + 1; } }

        /// <summary>
        /// prefab
        /// </summary>
        private UnityEngine.Object m_Prefab;

        /// <summary>
        /// 当前可用
        /// </summary>
        private Transform[] m_Instances = null;


        /// <summary>
        /// 挂的根结点
        /// </summary>
        private Transform m_Parent = null;

        /// <summary>
        /// 可用索引位置
        /// </summary>
        private int m_CurIndex;
        public int m_AsynCount = 1;
        public static Vector3 s_MoveOutPos = new Vector3(0f, 100000f, 0f);

        private Action<GameObject> m_SoomthDestroyHandler = null;
        private Action m_UnloadHandler = null;

        public PrefabReleaseDisappearType DisappearType { get; }

        private PrefabPoolWatcher m_PoolWatcher = null;
        public PrefabPoolWatcher PoolWatcher
        {
            get
            {
                if(null == m_PoolWatcher){
                    m_PoolWatcher = new PrefabPoolWatcher(this);
                }
                return m_PoolWatcher;
            }
        }

        public PrefabPool(UnityEngine.Object prefab, Transform parent, int cullAbove, bool smoothDestroy, Action<GameObject> SoomthDestroyHandler, Action unloadHandler, PrefabReleaseDisappearType dType = PrefabReleaseDisappearType.ActiveFalse)
        {
            m_Parent = parent;
            m_Prefab = prefab;
            m_CullAbove = cullAbove;
            m_SmoothDestroy = smoothDestroy;
            m_SoomthDestroyHandler = SoomthDestroyHandler;
            m_UnloadHandler = unloadHandler;
            m_Instances = new Transform[m_CullAbove];
            m_CurIndex = -1;
            DisappearType = dType;
        }

        /// <summary>
        /// 总数
        /// </summary>
        public int UnusedCount
        {
            get
            {
                if(m_CurIndex < 0)
                {
                    return 0;
                }
                return m_CurIndex + 1;
            }
        }

        public void PreDespawn(GameObject obj)
        {
            if(null == obj)
            {
                return;
            }

            if (obj.transform.parent != m_Parent)
            {
                obj.transform.SetParent(m_Parent, false);
            }

            if (DisappearType == PrefabReleaseDisappearType.ActiveFalse)
            {
                obj.transform.gameObject.SetActive(false);
            }
            else if(DisappearType == PrefabReleaseDisappearType.ScaleZero)
            {
                obj.transform.localScale = Vector3.zero;
            }
            else if(DisappearType == PrefabReleaseDisappearType.MoveOut)
            {
                obj.transform.localPosition = s_MoveOutPos;
            }
        }

        public void Despawn(GameObject obj)
        {
            if(null == obj)
            {
                return;
            }
            PreDespawn(obj);
            if (m_CurIndex + 1 < m_CullAbove)
            {
                m_Instances[++m_CurIndex] = obj.transform;
            }
            else
            {
                if (m_SmoothDestroy)
                {
                    m_SoomthDestroyHandler(obj);
                }
                else
                {
                    GameObject.Destroy(obj);
                }
            }
        }
        
        public GameObject Spawn()
        {
            Transform trm = null;
            //此时没有可用的
            if (m_CurIndex < 0)
            {   
                GameObject obj = GameObject.Instantiate(m_Prefab, m_Parent) as GameObject;
                trm = obj.transform;
            }
            else
            {
                trm = m_Instances[m_CurIndex];
                m_Instances[m_CurIndex] = null;
                --m_CurIndex;
            }
            if (null != trm)
            {
                if (DisappearType == PrefabReleaseDisappearType.ActiveFalse)
                {
                    trm.gameObject.SetActive(true);
                }
                else if (DisappearType == PrefabReleaseDisappearType.ScaleZero)
                {
                    trm.localScale = Vector3.one;
                }
                else if (DisappearType == PrefabReleaseDisappearType.MoveOut)
                {
                    trm.localPosition = Vector3.zero;
                }
                return trm.gameObject;
            }
            else
            {
                P.E("对象意外删除!");
                return null;
            }
        }


        /// <summary>
        /// 删除当前prefab池
        /// 不管是否引用全删除
        /// </summary>
        public void Destroy()
        {
            ClearUnused();
            m_Prefab = null;
            m_PoolWatcher?.Dispose();
            m_PoolWatcher = null;
            m_UnloadHandler?.Invoke();
        }

        /// <summary>
        /// 当内存吃紧时清除克隆好的没有用到的
        /// 是同步删除的
        /// </summary>
        public void ClearUnused()
        {
            if (m_CurIndex >= 0)
            {
                for (int i = m_CurIndex; i > -1; --i)
                {
                    if (null != m_Instances[i])
                    {
                        GameObject.Destroy(m_Instances[i].gameObject);
                        m_Instances[i] = null;
                    }
                }
                m_CurIndex = -1;
            }
        }

        //克隆才会要分时,而克隆分时可能用的时候pool不在了所以这里做个处理
        public class PrefabPoolWatcher
        {
            private PrefabPool m_Pool;
            public bool IsDisposed
            {
                get;
                private set;
            }
            public PrefabPoolWatcher(PrefabPool pool)
            {
                m_Pool = pool;
            }

            public GameObject Spawn()
            {
                if (IsDisposed || null == m_Pool)
                {
                    return null;
                }
                return m_Pool.Spawn();
            }
            public void Dispose()
            {
                IsDisposed = true;
                m_Pool = null;
            }
        }
    }
}